# -*- coding: utf-8 -*-
# @Time    : 2024/7/23 15:06
# @Author  : yujiahao
# @File    : 55_fastapi_response.py
# @description:OPENAPI 中的其他响应


# 在响应中可以附加其他状态代码，媒体类型、描述等等，对这些响应应该直接返回一个像JSONResponse一样的Response,并包含你的状态代码和内容


# todo model附加响应

# 可以向路径操作装饰器传递参数 responses 。
# 它接收一个 dict，键是每个响应的状态代码（如200），值是包含每个响应信息的其他 dict。
# 每个响应字典都可以有一个关键模型，其中包含一个 Pydantic 模型，就像 response_model 一样。
# FastAPI将采用该模型，生成其JSON Schema并将其包含在OpenAPI中的正确位置。

from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from starlette.responses import FileResponse


class Item(BaseModel):
    id: str
    value: str


class Message(BaseModel):
    message: str


app = FastAPI()


# 例如，要声明另一个具有状态码 404 和Pydantic模型 Message 的响应，可以写
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
    if item_id == "foo":
        return {"id": "foo", "value": "there goes my hero"}
    # 必须直接返回 JSONResponse
    return JSONResponse(status_code=404, content={"message": "Item not found"})


# 主响应的其他媒体类型
# todo 可以返回JSON对象或者png图像


@app.get(
    "/items_1/{item_id}",
    response_model=Item,
    responses={
        200: {
            "content": {"image/png": {}},
            "description": "Return the JSON item or an image.",
        }
    },
)
async def read_item_1(item_id: str, img: bool | None = None):
    # 这样的话必须直接使用 FileResponse 返回图像。
    # 除非在 responses 参数中明确指定不同的媒体类型，否则FastAPI将假定响应与主响应类具有相同的媒体类型（默认为application/json ）。
    # 但是如果您指定了一个自定义响应类，并将 None作为其媒体类型，FastAPI将使用 application/json 作为具有关联模型的任何其他响应。
    if img:
        return FileResponse("image.png", media_type="image/png")
    else:
        return {"id": "foo", "value": "there goes my hero"}


# todo 组合信息


"""
还可以联合接收来自多个位置的响应信息，包括 response_model、 status_code 和 responses 参数。

可以使用默认的状态码 200 （或者您需要的自定义状态码）声明一个 response_model，然后直接在 OpenAPI 模式中在 responses 中声明相同响应的其他信息。

FastAPI 将保留来自 responses 的附加信息，并将其与模型中的 JSON Schema 结合起来。
"""


@app.get(
    "/items_2/{item_id}",
    response_model=Item,
    responses={
        404: {"model": Message, "description": "The item was not found"},
        200: {
            "description": "Item requested by ID",
            "content": {
                "application/json": {
                    "example": {"id": "bar", "value": "The bar tenders"}
                }
            },
        },
    },
)
async def read_item_2(item_id: str):
    if item_id == "foo":
        return {"id": "foo", "value": "there goes my hero"}
    else:
        return JSONResponse(status_code=404, content={"message": "Item not found"})


# todo 联合预定义响应和自定义响应


# 您可能希望有一些应用于许多路径操作的预定义响应，但是你想将不同的路径和自定义的相应组合在一块。
# 对于这些情况，你可以使用Python的技术，将 dict 与 **dict_to_unpack 解包

responses = {
    404: {"description": "Item not found"},
    302: {"description": "The item was moved"},
    403: {"description": "Not enough privileges"},
}


@app.get(
    "/items_3/{item_id}",
    response_model=Item,
    # 这里涉及到一个解包的操作
    responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item_3(item_id: str, img: bool | None = None):
    if img:
        return FileResponse("image.png", media_type="image/png")
    else:
        return {"id": "foo", "value": "there goes my hero"}
